package xml;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;


import measures.DistanceSet;

/**
 * @author Thomas Wangler, wangler_thomas@hotmail.com
 * Translation Memories for patent translation
 * CreateXML
 * The methods of the class CreateXML compute a xml for given input.
 * Words that can be translated stand in xml tags e.g. <xml translation = "translated_words"> x <\xml>.
 * Words that the fuzzy match doesn´t contain are written as single words without xml-tags.
 */
public class CreateXML {

	private String xmlstring;
	private String sentTest;
	private String sentTrain_de;
	private String sentTrain_en;
	private String alignmentTrain;
	
	private ArrayList<String> matchPath;
	private ArrayList<String> sourceList;
	private ArrayList<String> tmsourceList;
	private ArrayList<String> tmtargetList;

	
	private HashMap<Integer, List<Integer>> alignmentMap;
	
	private DistanceSet distanceSet;
 
	
	
	/**
	 * Constructor for CreateXML Objects with values for test sentence, trainings sentences in German 
	 * and English as well as alignments between this trainings sentences.
	 * Creates a matchPath via distanceSet (levenshtein distance between sentTest (=source) and 
	 * sentTrain_de (=tmsource). 
	 */
	public CreateXML(String str){
		
		String [] input = str.split("\t");
		this.sentTest = input[0].split("\\|\\|\\|")[1];
		this.sentTrain_de = input[1].split("\\|\\|\\|")[1];
		this.sentTrain_en = input[2].split("\\|\\|\\|")[1];
		this.alignmentTrain = input[3].split("\\|\\|\\|")[1];
		
		this.distanceSet = new measures.DistanceSet(this.sentTest, this.sentTrain_de);
		
		this.matchPath = this.distanceSet.getBacktrace();
		
		mapAlignment();
		readSentsIntoList();
	}
	
	/**
	 * This method just prints out: the source, tmsource, tmtarget and alignment.
	 */
	public void printsentLists(){
		
		//System.out.println(this.sentTest.length());
		//System.out.println(this.sentTrain_de.length());
		//System.out.println(this.sentTrain_en.length());
		
		//System.out.println(this.matchPath.toString() + " " + this.matchPath.size());
		
		System.out.println(this.sourceList.toString());
		System.out.println(this.tmsourceList.toString());
		System.out.println(this.tmtargetList.toString());
		
		System.out.println(this.alignmentMap.toString());

	}
	
	/**
	 * A function call to provide the translation.
	 * @return Returns the translatedXML as String.
	 */
	public String getTranslation(){
		return this.translateXML();
	}
	
	/**
	 * Creates the alignment as HashMap.
	 * If the map already contains the given key, the current key will be appended.
	 * Else: a new key-value pair is inserted in the HashMap
	 */
	private void mapAlignment(){
		
		this.alignmentMap = new HashMap<Integer, List<Integer>>();
		
		String[] splitAlign = alignmentTrain.split(" ");
		
		for(String s: splitAlign){
			
			String[] splitSoTa = s.split("-");
			if(this.alignmentMap.containsKey(Integer.parseInt(splitSoTa[0]))){
				this.alignmentMap.get(Integer.parseInt(splitSoTa[0])).add(Integer.parseInt(splitSoTa[1]));
			}else{
				this.alignmentMap.put(Integer.parseInt(splitSoTa[0]), new ArrayList<Integer>(Arrays.asList(Integer.parseInt(splitSoTa[1]))));
			}
						
		}
		
	}
	
	/**
	 * Creates finally two lists: sourceList and tmsourceList.
	 * Therefore it will be differentiate about following cases:
	 * Matching: sourceList as well as tmsourceList get the correspondent value.
	 * Insertion: sourceList gets a null-value, tmsourceList the correspondent value from tmsource.
	 * Deletion: sourceList gets the correspondant value from source, tmsource a null-value.
	 */
	private void readSentsIntoList(){
		
		sourceList = new ArrayList<String>();
		tmsourceList = new ArrayList<String>();
		tmtargetList = new ArrayList<String>();
		
		
		ArrayList<String> sSplit = new ArrayList<String>(Arrays.asList(this.sentTest.split(" ")));
		ArrayList<String> tmsSplit = new ArrayList<String>(Arrays.asList(this.sentTrain_de.split(" ")));
		ArrayList<String> tmtSplit = new ArrayList<String>(Arrays.asList(this.sentTrain_en.split(" ")));
		
		this.tmtargetList = tmtSplit;
		
		for(int i = 0; i < this.matchPath.size(); i++){
			
			if(this.matchPath.get(i).equals(" Match ") || this.matchPath.get(i).equals(" Replacement ")){
				
				this.sourceList.add(sSplit.get(i));
				this.tmsourceList.add(tmsSplit.get(i));
					
			}
			if(this.matchPath.get(i).equals(" Insertion ")){	
				this.sourceList.add(null);
				sSplit.add(i, null);
				
				this.tmsourceList.add(tmsSplit.get(i));
				
			}
			if(this.matchPath.get(i).equals(" Deletion ")){	
				
				this.sourceList.add(sSplit.get(i));
				
				this.tmsourceList.add(null);
				tmsSplit.add(i, null);				
			}	
		}
		
	}
	/**
	 * Constructs the translation in a xml-frame.
	 * MapOffset makes sure that tmsource has the right index for the alignment data. 
	 * It is necessary because of constructing tmsource with null values. 
	 * @return Returns the translated XML as String.
	 */
	private String translateXML(){
		
		xmlstring = new String();
		
		int i = 0;
		Boolean done = false;
		int mapOffset = 0;
		
		
		while(!done){
			
			if(i>this.matchPath.size()-1){
				done = true;
			}
			
			if(!done && this.matchPath.get(i).equals(" Match ")){
			
				if(i>0){
					xmlstring += " ";
				}
				xmlstring += "<xml translation=\"";
			
					while(i<this.matchPath.size() && this.matchPath.get(i).equals(" Match ")){
						
						if(this.alignmentMap.containsKey(i-mapOffset)){
							for(int value: this.alignmentMap.get(i-mapOffset)){	
								
								if(!this.tmtargetList.get(value).equals("<s>") && !this.tmtargetList.get(value).equals("</s>")){
									xmlstring += " ";
									xmlstring += this.tmtargetList.get(value);
									
									//removing already written entries to prevent multiple instances of the tmtarget-token in case of multiple alignment assignments
									this.tmtargetList.set(value, "");
									
								}
							}
						}
						i++;
					}
			
				xmlstring += "\"> x </xml>";
			
				}
			
			else{
				
					while(i<this.matchPath.size() && !this.matchPath.get(i).equals(" Match ")){
						if(this.matchPath.get(i).equals(" Replacement ")){
						
							if(i>0){
								xmlstring += " ";
							}
							xmlstring += this.sourceList.get(i);
							
							i++;
						
						}else{
							if(this.matchPath.get(i).equals(" Deletion ")){
								
								mapOffset ++;
								xmlstring += " ";
								xmlstring += this.sourceList.get(i);
								i++;
							
							}else{
								if(this.matchPath.get(i).equals(" Insertion ")){
									i++;
							}
						}	
					}			
				}
			}
		}	
		
		
		
		//handling spaces
		this.xmlstring = xmlstring.replace("translation=\" ", "translation=\"");
		
		//handling single deletion case
		this.xmlstring = xmlstring.replace("\"> x </xml> <xml translation=\"", " ");
		
		//handling spaces
		this.xmlstring = xmlstring.replace("<xml translation=\"\"> x </xml>\"", "");
			
		this.xmlstring = xmlstring.replace("<xml translation=\"\"> x </xml>", "");
		
		//truncating double spaces into single spaces
		this.xmlstring = xmlstring.replace("  " , " ");
		
		return this.xmlstring;
	}
	
}
